In Kapitel 15, »Dateien und E/A«, haben wir uns damit beschäftigt, wie man in Perl aus Dateien liest oder in Dateien schreibt. Heute wollen wir uns weiteren Aspekten der Dateiverwaltung zuwenden - insbesondere den verschiedenen Möglichkeiten, mit denen Sie über Ihr Skript das Dateisystem beeinflussen können. Heute lernen Sie,
Mit Perl können Sie nicht nur aus Dateien lesen und in Dateien schreiben, sondern Sie können die Dateien auch verwalten - so wie Sie es üblicherweise von der Befehlszeile oder einem Datei-Manager aus tun: Sie können Dateien umbenennen, löschen, Zugriffsrechte ändern oder Verknüpfungen zu Dateien erstellen. Für jede dieser Aufgaben greifen Sie auf verschiedene vordefinierte Perl-Funktionen zurück, die ich Ihnen in diesem Abschnitt vorstellen möchte.
Um einen Dateinamen durch einen anderen zu ersetzen, ohne dabei den Inhalt der
Datei zu beschädigen, rufen Sie die Funktion rename
mit zwei Argumenten auf: dem
alten Namen der Datei und dem neuen Namen:
rename meinedatei, meinedatei.bak;
Existiert bereits eine Datei mit dem gleichen Namen wie die neue Datei, wird diese
von Perl überschrieben. Sind Sie an Unix gewöhnt, stellt dies kein Problem für Sie
dar. Arbeiten Sie jedoch auf einem Windows- oder Mac-Rechner, gilt es vorsichtig zu
sein! Es gibt keine Sicherheitsabfragen, wenn Sie eine bestehende Datei
überschreiben. Deshalb sollten Sie vielleicht besser sicherstellen, dass es für den neuen
Namen nicht bereits eine gleichnamige Datei gibt (mit einem der Dateitests wie -e
),
bevor Sie Ihre Datei umbenennen.
Die rename
-Funktion liefert 1
oder 0
zurück (wahr oder falsch), je nachdem ob Sie Ihre
Datei erfolgreich umbenennen konnten oder nicht. Um eine Datei umbenennen zu
können, müssen Ihnen (oder dem Benutzer Ihres Skripts) die entsprechenden
Zugriffsrechte eingeräumt sein.
In der Unix-Version von Perl gibt es zwei Funktionen zum Erstellen von
Verknüpfungen zwischen Dateien: link
und symlink
, mit denen harte
beziehungsweise symbolische Verknüpfungen (entsprechend dem Unix-Befehl ln
)
erstellt werden. Harte Verknüpfungen dienen dazu, eine Datei mit mehr als einem
Namen zu erzeugen. Wenn Sie einen beliebigen dieser Namen (einschließlich der
Originaldatei) löschen, existiert die Datei weiter - solange wie es noch Namen für die
Datei gibt. Symbolische Verknüpfungen sind Verweise auf andere Dateien. Wird die
Verknüpfung gelöscht, bleibt die Datei bestehen, löschen Sie hingegen die
Originaldatei, zeigt die symbolische Verknüpfung ins Leere. Nicht alle Unix-Systeme
unterstützen symbolische Verknüpfungen.
Beide Funktionen, link
und symlink
, erfordern zwei Argumente: den Name der
Originaldatei, für die eine Verknüpfung erstellt werden soll, und den Namen der
neuen Verknüpfung. Sehen Sie dazu folgendes Beispiel:
link datei1, datei2;
Beide Funktionen liefern im Erfolgsfall 1
(wahr) und ansonsten 0
(falsch) zurück.
Weder hart noch symbolisch verknüpfte Dateien stellen für Perl ein Problem dar.
Wenn Sie open
mit einem Dateinamen verwenden, der eine Verknüpfung darstellt,
wird statt der Verknüpfung die Originaldatei geöffnet. Mit Hilfe des Dateitests -l
können Sie prüfen, ob eine bestimmte Datei eine symbolische Verknüpfung ist oder
nicht. Anschließend können Sie die Verknüpfung mit Hilfe der Funktion readlink
zu
der ursprünglichen Position der Datei zurückverfolgen:
if (-l $dat) { # dat ist eine Verknüpfung
$ursprungsdatei = readlink $dat; # ermittelt die echte Datei
}
Beachten Sie, dass Sie mit readlink
nur den relativen Pfadnamen der Originaldatei zu
ihrer symbolischen Verknüpfung erhalten. Das bedeutet, dass Sie entweder diesen
Pfad zu einer vollen Pfadangabe erweitern oder in das Verzeichnis mit der
symbolischen Verknüpfung wechseln müssen, um den Pfadnamen der Originaldatei
zu verwenden. Wie Sie Verzeichnisse wechseln, erfahren Sie weiter hinten in diesem
Kapitel.
Verwenden Sie Perl für Windows? Dann können Sie weder mit harten noch mit
symbolischen Verknüpfungen arbeiten. Sie können aber das Modul Win32::Shortcut
verwenden, um Windows-Explorer-Verknüpfungen zu erzeugen.
In MacPerl gibt es zwar keine Entsprechung zu den harten Verknüpfungen, doch
funktionieren die symbolischen Verknüpfungen ganz analog zu den Mac-Aliasen. Die
Funktion symlink
erzeugt einen Alias, der Dateitest -l
liefert wahr zurück, wenn die
Datei ein Alias ist und readlink
ermittelt die Position der Datei, mit der der Alias
verknüpft ist.
Sie sind mit einer Datei fertig und sind sich sicher, dass Sie sie nicht weiter benötigen.
Wie werden Sie diese Datei jetzt wieder los? Dazu steht Ihnen die Funktion unlink
zur
Verfügung (trotz ihres Namens entfernt sie sowohl Verknüpfungen als auch Dateien).
Sie entspricht in ihrer Funktionsweise dem Unix-Befehl rm
oder dem DOS-Befehl del
.
Allerdings verschiebt diese Funktion die Datei nicht in einen Papierkorb wie unter
Windows oder beim Mac. Wird eine Datei gelöscht, wird sie unwiederbringlich
entfernt. Deshalb sollten Sie Vorsicht walten lassen, um nicht aus Versehen Dateien
zu löschen, die Sie eigentlich noch benötigen.
Die Funktion unlink
übernimmt ein Listenargument, bei dem es sich entweder um
einen einzigen Dateinamen oder um eine ganze Liste von Dateinamen handeln kann.
Als Rückgabe liefert Sie die Anzahl der gelöschten Dateien zurück.
unlink temp, config, foo;
Beachten Sie, dass Perl auf manchen Systemen - allen voran Unix - bedenkenlos auch Nur-Lesen-Dateien löscht. Vorsicht ist also geboten, denn der Nur-Lesen-Status einer Datei bezieht sich lediglich darauf, dass der Inhalt dieser Datei nicht geändert werden darf, und nicht darauf, ob der Dateiname verschoben oder gelöscht wird.
Wird die Funktion unlink
auf eine harte Verknüpfung angewendet, wird diese
Verknüpfung entfernt. Andere harte Verknüpfungen zu der Datei bleiben bestehen.
Auch im Falle einer symbolischen Verknüpfung wird die Verknüpfung entfernt, die
Originaldatei bleibt bestehen. Mit readlink
können Sie symbolischen Verknüpfungen
nachfolgen. Beachten Sie, dass nach dem Löschen einer Datei, zu der symbolische
Verknüpfungen bestehen, alle symbolischen Verknüpfungen zu der Datei ins Leere
zielen.
Sie können mit unlink
keine Verzeichnisse entfernen. Wie Sie Verzeichnisse löschen,
erfahren Sie im Abschnitt »Verzeichnisse verwalten und wechseln«.
Es gibt noch eine Reihe weiterer dateibezogener Funktionen, die ich Ihnen kurz vorstellen möchte. Eine ausführlichere erspare ich mir, da sich die betreffenden Operationen von Plattform zu Plattform unterscheiden (oder unter Umständen unter Windows oder Mac gar nicht existieren). In Tabelle 17.1 sind weitere Dateiverwaltungsfunktionen aufgeführt, die auf Unix-Systemen verfügbar sind und mit Unix-Befehlen korrspondieren. Wenn Sie eine der Funktionen aus Tabelle 17.1 verwenden möchten, schauen Sie in der Dokumentation zu Ihrer Perl-Version nach, ob diese Funktion unterstützt wird oder nicht (oder ob es für die betreffende Operation anderweitig Unterstützung gibt - zum Beispiel durch ein plattformspezifisches Modul).
Weitere Informationen zu diesen Funktionen finden Sie in der perlfunc-Manpage.
Tabelle 17.1: Weitere Dateiverwaltungsfunktionen
Um unter Windows und Windows NT Dateizugriffsberechtigungen und -attribute zu
verwalten, können Sie auch die Module win32::File
und win32::FileSecurity
verwenden. Diese ermöglichen es Ihnen, Dateiattribute und NT-Nutzerrechte zu
prüfen und zu ändern.
So wie Sie in Ihren Perl-Skripten mit Dateien in der gleichen Weise arbeiten, wie Sie es von der Shell oder der Befehlszeile aus tun, so können Sie in Ihren Perl-Skripten auch durch das Dateisystem navigieren und Verzeichnisse verwalten (im Sprachgebrauch mancher Betriebssysteme spricht man auch von Ordnern statt von Verzeichnissen). Sie können das aktuelle Verzeichnis ermitteln, das aktuelle Verzeichnis wechseln, den ganzen Inhalt des Verzeichnisses oder einen Teilbereich davon auflisten, und Sie können Verzeichnisse selbst anlegen oder löschen. In diesem Abschnitt möchte ich auf einige Aspekte bei der Ausführung dieser Operationen mit Perl eingehen.
Jedes Perl-Skript kennt sein aktuelles Arbeitsverzeichnis, das heißt das Verzeichnis aus dem das Skript aufgerufen wurde (wenn Sie gewohnt sind, mit einem befehlszeilenorientierten System zu arbeiten, wird Sie dies nicht überraschen). Auf dem Mac, der mit Droplet-Skripten arbeitet, ist das aktuelle Arbeitsverzeichnis das Verzeichnis, in dem sich das Droplet selbst befindet.
Um das aktuelle Verzeichnis von einem Perl-Skript aus zu ändern, verwenden Sie die
Funktion chdir
:
chdir "bilder";
Wie schon bei der Verwendung von Verzeichnisnamen im Aufruf der open
-Funktion
sollten Sie darauf achten, dass Sie die Pfadangaben korrekt angeben und die
Eigenheiten der verschiedenen Systeme berücksichtigen. Relative Pfadangaben, wie in
diesem Beispiel, sind in Ordnung. Obiges Beispiel ändert das aktuelle
Arbeitsverzeichnis in das Verzeichnis bilder
, das unter dem gleichen Verzeichnis
angeordnet ist, aus dem das Skript ursprünglich aufgerufen wurde (puh!).
Ist es möglich, herauszufinden, in welchem Verzeichnis Sie sich momentan befinden?
Ja, aber wie Sie dazu vorgehen, hängt von dem System ab, auf dem Sie gerade
arbeiten. Unter Unix stellen Sie das aktuelle Verzeichnis fest, indem Sie die schrägen
Anführungsstrich-Operatoren verwenden, um den Befehl pwd
aufzurufen (mehr zu den
schrägen Anführungszeichen in Kapitel 18, »Perl und das Betriebssystem«):
$curr =`pwd`;
Diese Methode läßt sich auch auf dem Mac anwenden. Unter Windows erhalten Sie
das aktuelle Arbeitsverzeichnis mit der Funktion Win32::GetCWD
. Der beste Weg, das
aktuelle Arbeitsverzeichnis zu ermitteln - ein Weg, der übrigens auf allen Plattformen
gleich gut funktioniert - führt allerdings über das Modul Cwd
, das Teil der
Standardbibliothek ist. Das Cwd
-Modul stellt Ihnen die cwd
-Funktion zur Verfügung, die
das aktuelle Arbeitsverzeichnis zurückliefert (unter Unix führt cwd
übrigens einfach
`pwd`
aus, so dass Sie auch hier bezüglich des Ergebnisses sicher sein können):
use Cwd;
$curr = cwd();
In Perl gibt es zwei Wege, eine Liste der Dateien in einem Verzeichnis erstellen zu
lassen: Die erste Methode verwendet Platzhalter, mit deren Hilfe Sie eine Liste von
Dateien erzeugen können, die einem bestimmten Muster entsprechen. Die zweite
Methode verwendet die Funktionen opendir
und readdir
, die beide eine komplette
Liste aller Dateien in einem Verzeichnis ausgeben.
Diese Technik, die man im Englischen als Datei-»Globbing« bezeichnet, erlaubt es
Ihnen, eine Liste aus Dateien des aktuellen Verzeichnisses zu erstellen, die einem
bestimmten einfachen Muster entsprechen. Wenn Sie jemals mit einem
befehlszeilenorientierten System gearbeitet und Dateien mit Mustern wie *.txt
oder
*foo*
aufgelistet haben, dann haben Sie schon mit Datei-Globbing gearbeitet (auch
wenn Sie vielleicht nicht wußten, dass man diese Technik so nennt).
Der Begriff »glob« ist - falls Sie es nicht schon geraten haben - von Unix entlehnt. Verwenden Sie niemals einen einfachen Begriff, wenn es auch kompliziert geht.
Beim Datei-Globbing nutzen Sie das Platzhalterzeichen *
, um
einen bestimmten Satz
von Dateinamen zu definieren. Verwechseln Sie das Datei-Globbing aber nicht mit
den regulären Ausdrücken - beim Datei-Globbing steht das *
für ein beliebiges
Zeichen (keines oder mehrere), und es ist das einzige Sonderzeichen, das Sie
verwenden können. Als Ergebnis erhalten Sie eine Auflistung aller Dateinamen in dem
aktuellen Verzeichnis, die dem Muster entsprechen. So liefert zum Beispiel *.html
alle
Dateinamen zurück, die auf die Extension .html
auslauten, und *foo*
alle Dateien,
die im Namen selbst die Zeichenfolge foo
enthalten. Sie können diese Dateinamen
dann verwenden, um auf den einzelnen Dateien bestimmte Operationen auszuführen.
Perl erzeugt Datei-Globs auf zwei Wegen: mit dem Operator <>
, der zwar aussieht wie
der Eingabeoperator, aber keiner ist, oder mit der glob
-Funktion.
Wenn Sie den <>
-Operator verwenden, müssen Sie das Muster innerhalb der spitzen
Klammern angeben:
@dateien = <*.pl>;
Dieser Ausdruck, bei dem <>
in einem Listenkontext verwendet wird, liefert eine Liste
aller Dateien mit der Extension .pl
zurück. Wenn Sie <>
in einem skalaren Kontext
verwenden, erhalten Sie jeden Dateinamen separat (wie bei der Eingabe), und wenn
Sie den Operator innerhalb einer while
-Schleife verwenden, werden die Dateinamen
nacheinander der $_
-Variablen zugewiesen, ebenfalls wie bei der Eingabe).
So gibt zum Beispiel das folgende Codebeispiel eine Liste aller Dateien in dem
aktuellen Verzeichnis aus, die die Extension .txt
haben:
while (<*.txt>) {
print $_, "\n";
}
Verwechseln Sie den Globbing-Operator <>
nicht mit dem <>
-Operator für die
Eingabe - sie sehen nur äußerlich gleich aus, arbeiten aber in ganz unterschiedlicher
Weise. Letzterer benötigt einen Datei-Handle als Argument und liest den Inhalt dieser
Eingabedatei. Ersterer liefert Strings mit den Dateinamen aus dem aktuellen
Verzeichnis, die dem Muster entsprechen.
Da man die beiden Operatoren für das Globbing und für die Eingabe leicht
verwechseln kann, gibt es noch die glob
-Funktion. Damit wird das Globbing für alle
deutlich und die Verwechslungsgefahr gebannt. Bei der glob
-Funktion müssen Sie das
Filtermuster in Anführungszeichen setzen:
@dateien = glob "*.pl";
Das Ergebnis ist das gleiche. Auch hier erhalten Sie eine Liste der Dateien, die auf .pl
enden (in einem skalaren Kontext erhalten Sie die Dateien eine nach der anderen). In
modernen Perl-Skripten sollte man dem Datei-Globbing mit der glob
-Funktion den
Vorzug geben.
Es gibt noch einen zweiten Weg, um eine Liste der Dateien in einem Verzeichnis zu
erhalten, und der führt über die Verzeichnis-Handles. Diese erinnern stark an die
Datei-Handles und verhalten sich auch so. Sie beziehen sich aber ausschließlich auf
Verzeichnisse. Wenn Sie ein Verzeichnis mit Hilfe eines Verzeichnis-Handles einlesen,
erhalten Sie eine vollständige Liste aller Dateien in dem Verzeichnis, einschließlich der
versteckten Dateien, die mit einem Punkt beginnen (die Sie durch Datei-Globs nicht
erhalten), sowie .
und ..
für das aktuelle und das übergeordnete Verzeichnis in Unix
und Windows (der Mac erkennt diese Zeichen nicht als Elemente des aktuellen
Verzeichnisses; Sie können statt dessen :
für das aktuelle und ::
für das
übergeordnete Verzeichnis verwenden).
Verzeichnis-Handles werden auf die gleiche Art und Weise geöffnet und geschlossen
wie Datei-Handles, nur dass man die Funktionen opendir
und closedir
verwendet.
Die Funktion opendir
übernimmt zwei Argumente: den Namen eines Verzeichnis-
Handles (von Ihnen gewählt) und das Verzeichnis, das ausgegeben werden soll.
opendir(DIR, ".") or die "Verzeichnis kann nicht geöffnet werden: $!\n";
Wie bei der open
-Funktion sollten Sie stets das Ergebnis prüfen und das Skript
gegebenenfalls mit Hilfe der die
-Funktion in Würde sterben lassen (die Variable $!
ist
hier ebenfalls sehr hilfreich).
Bezüglich der Namensgebung gelten für die Verzeichnis-Handles (hier DIR
) die
gleichen Regeln wie für die Datei-Handles. Verzeichnis-Handles werden vollkommen
unabhängig von den Datei-Handles betrachtet - Sie können einem Verzeichnis-
Handle den gleichen Namen geben wie einem Datei-Handle, es wird keine Konflikte
geben.
Sobald ein Verzeichnis-Handle geöffnet ist, können Sie mit readdir
daraus lesen. Wie
schon der Eingabeoperator <>
liefert auch readdir
in einem skalaren Kontext einen
einzelnen Dateinamen zurück und in einem Listenkontext eine Liste aller Dateinamen.
Das folgende Codefragment öffnet zum Beispiel das aktuelle Verzeichnis (das ».
«-
Verzeichnis unter Unix und Windows; Mac-Anwender schreiben statt dessen »:
«) und
gibt eine Liste des Verzeichnisses aus:
opendir(CURR, ".") or die "Verzeichnis kann nicht geöffnet werden: $!\n";
while (defined($file = readdir(CURR))) {
print "$file\n";
}
Beachten Sie, dass readdir
- im Gegensatz zum Eingabeoperator <>
- keine
automatischen Zuweisungen an die Variable $_
vornimmt. Sie müssen den Code dazu
selbst in die while
-Schleife aufnehmen oder (wie ich) eine temporäre Variable
verwenden.
Mit readdir
erhalten Sie eine Liste aller Dateien und Unterverzeichnisse in dem
Verzeichnis. Wenn Sie bestimmte Dateien (versteckte Dateien, .
oder ..
) herausfiltern
wollen, müssen Sie dazu einen Vergleich mit einem regulären Ausdruck anschließen.
Sobald Sie das Verzeichnis-Handle nicht mehr benötigen, können Sie es mit der
closedir
-Funktion schließen:
closedir(CURR);
Sie können aus Ihren Perl-Skripten heraus sogar neue Verzeichnisse anlegen und
nicht mehr benötigte Verzeichnisse löschen. Die Funktionen dazu lauten mkdir
und
rmdir
.
Die Funktion mkdir
übernimmt zwei Argumente: den Namen eines Verzeichnisses
(oder Ordners) und einen Satz an Zugriffsberechtigungen. Unter Unix beziehen sich
die Zugriffsberechtigungen auf die Standardzugriffsbits des chmod
-Befehl im
Oktalformat (0 plus drei Zahlen von 0 bis 7). Unter Windows und Mac ist das
Argument der Zugriffsberechtigung zwar gefordert, aber nicht sinnvoll. Verwenden
Sie einfach 0777
als Argument, und Sie dürften keine Probleme bekommen:
mkdir temp, 0777;
0777
ist eine Oktalzahl, die sich auf die Unix-Zugriffsberechtigungsbits bezieht und Lese-, Schreib- und Ausführungsberechtigungen für die Welt, die Gruppe und den Besitzer bedeutet. Auf dem Mac und unter Windows werden andere Formen von Zugriffsberechtigungen für Dateien und Verzeichnisse verwendet, so dass Sie das Argument der Zugriffsberechtigung anmkdir
hier eigentlich nicht brauchen (dennoch müssen Sie einen Wert übergeben, und deshalb ist0777
eine gute, allgemein sinnvolle Wahl.)
Die mkdir
-Funktion erzeugt das neue Verzeichnis in dem aktuellen Arbeitsverzeichnis
des Skripts. Sie können das Verzeichnis aber auch überall sonstwo anlegen, indem Sie
den vollen Pfadnamen angeben, zuerst das aktuelle Verzeichnis wechseln oder - falls
Sie Spaß an ausgefallenem Code haben - die Möglichkeiten des Moduls File::Path
nutzen, das Teil der Standardbibliothek ist und Funktionen zum Erstellen und Löschen
ganzer Verzeichnisunterbereiche enthält.
Zum Entfernen eines Verzeichnisses gibt es den Befehl rmdir
.
rmdir temp;
Das Verzeichnis muss jedoch leer sein (das heißt es darf keine Dateien enthalten), damit Sie es löschen können.
Jetzt möchte ich Ihnen ein richtig nützliches Beispiel präsentieren, das ich selbst recht
häufig verwende: Wenn Sie ein Verzeichnis voller GIF- oder JPEG-Grafiken haben (in
dem Verzeichnis also Dateien mit den Extension .gif
, .jpeg
oder .jpg
stehen),
können Sie mit Hilfe dieses Skripts eine HTML-Datei namens index.html
erzeugen,
die Verknüpfungen zu diesen Dateien enthält. Auf diesem Wege können Sie schnell
eine große Menge an Grafikdateien in das Web stellen, ohne erst viel Zeit mit dem
Anlegen der HTML-Datei zu verschwenden (wenn Sie möchten, können Sie die Datei
selbstverständlich danach noch nachbessern).
Das Skript, das ich für diese Aufgabe geschrieben habe, verwendet Datei-Handles, um
die Datei index.html
zu öffnen und in die Datei zu schreiben, Verzeichnis-Handles,
um den Inhalt des aktuellen Verzeichnisses einzulesen, und Datei-Tests mit regulären
Ausdrücken, um die Dateien herauszufiltern, nach denen wir suchen. In Listing 17.1
sehen Sie das Ergebnis:
Listing 17.1: Das Skript imagegen.pl
1: #!/usr/bin/perl -w
2: use strict;
3: use Cwd;
4:
5: open(OUT, ">index.html") or
die "Index-Datei kann nicht geöffnet werden: $!\n";
6: &printhead();
7: &processfiles();
8: &printtail();
9:
10: sub printhead {
11: my $curr = cwd();
12: print OUT "<HTML>\n<HEAD>\n";
13: print OUT "<TITLE>Bilddateien aus dem Verzeichnis $curr</TITLE>\n";
14: print OUT "</HEAD>\n<BODY>\n";
15: print OUT "<H1>Bilddateien</H1>\n";
16: print OUT "<P>";
17: }
18:
19: sub processfiles {
20: opendir(CURRDIR, '.') or
die "Verzeichnis kann nicht geöffnet werden ($!),
exiting.\n";
21: my $file = "";
22:
23: while (defined($file = readdir(CURRDIR))) {
24: if (-f $file and $file =~ /(\.gif|\.jpe?g)$/i) {
25: print OUT "<A HREF=\"$file\">$file</A><BR>\n";
26: }
27: }
28: closedir(CURRDIR)
29: }
30:
31: sub printtail {
32: print OUT "</BODY></HTML>\n";
33: close(OUT);
34: }
Wir beginnen, indem wir die Ausgabedatei (index.html
) in Zeile 5 öffnen. Achten Sie
auf das >
-Zeichen, es zeigt Ihnen an, dass es sich hier um ein Handle für eine
Ausgabedatei handelt.
Die Subroutine &printhead()
in den Zeilen 10 bis 17 gibt lediglich den oberen Teil
der HTML-Datei aus. Die einzige Schwierigkeit dabei besteht darin, das aktuelle
Verzeichnis in den Titel der Seite aufzunehmen. Zu diesem Zweck wird die Funktion
cwd()
aufgerufen (die in Zeile 3 aus dem Cwd
-Modul importiert wurde). Ich habe hier
auf die Verwendung eines Hier-Dokuments für die Ausgabe des HTML-Codes
verzichtet, denn es wären dann mit Sicherheit nur noch mehr Zeilen geworden, als es
ohnehin schon sind.
In der Subroutine &processfiles()
(Zeile 19 bis 29) findet die eigentliche Arbeit statt.
Zeile 20 öffnet das aktuelle Verzeichnis (Mac-Anwender müssen das ».
« in »:
« ändern).
Daran schließt sich in den Zeilen 23 bis 26 eine while
-Schleife an, die jeden
Dateinamen im Verzeichnis durchläuft. Der Test in Zeile 24 prüft, ob es sich bei der
Datei auch tatsächlich um eine Datei und nicht um ein Verzeichnis handelt (der Test -
f
), und filtert mit Hilfe eines regulären Ausdrücks die Grafikdateien heraus (jene mit
der Extension .gif
, .jpeg
und .jpg
). Handelt es sich bei der aktuellen Datei wirklich
um eine Grafikdatei, geben wir in Zeile 25 eine Verknüpfung zu dieser Datei aus,
wobei wir den Dateinamen als Verknüpfungstext verwenden.
Beachten Sie, dass Sie durch die Verwendung der Dateinamen als Verknüpfungstexte nicht unbedingt zu sehr aussagekräftigen Verknüpfungen kommen - wenn Sie Pech haben, lauten Ihre Verknüpfungen »bild1.gif«, »bild2.gif«, »bild3.gif« und so weiter. Nicht sehr aussagestark, aber zumindest ein Anfang. Nachdem das Skript ausgeführt wurde, können Sie ja die HTML-Datei bearbeiten und aussagekräftigere Verknüpfungen erstellen (»netter Sonnenaufgang«, »Osterumzug«, »Bill verhält sich dumm« und so weiter).
Als Abschluß des Skripts rufen wir die Subroutine &printtail()
auf, die das Ende der
HTML-Datei ausgibt und den Ausgabedatei-Handle schließt.
Zusätzlich zu den Funktionen, die ich in dieser Lektion und in Kapitel 15 beschrieben habe und die allesamt in der perlfunc-Manpage beschrieben sind, gibt es in der Standard-Modulbibliothek noch eine Reihe weiterer Module mit Funktionen zum Verwalten von Dateien und Datei-Handles.
Viele dieser Module sind einfach objektorientierte Hüllen zu den Standard- Dateioperationen, andere wiederum sind besonders nützlich, um plattformübergreifende Probleme zu lösen oder zu umgehen. Wieder andere stellen einfach Funktionen zur Verfügung, die die Verwaltung von Dateien und Verzeichnissen leichter machen.
Einige dieser Module habe ich bereits erwähnt - beispielsweise Getopt
und Cwd
. In
Tabelle 17.2 finden Sie eine ausführlichere Liste. Details zu den einzelnen Modulen
finden Sie in der perlmod-Manpage.
Tabelle 17.2: Dateibezogene Module
Ein Perl-Skript ist keine Insel. Es kommt der Punkt, da ist es sehr wahrscheinlich, dass Ihr Skript sich mit der äußeren Welt des Dateisystems beschäftigen muss, besonders dann, wenn Ihr Skript extensiv Dateien liest und in Dateien schreibt. In Kapitel 15 haben Sie gelernt, wie man den Inhalt einer Datei liest und in eine Datei schreibt. Heute haben wir uns den globaleren Themen der Datei- und Verzeichnisverwaltung innerhalb von Perl-Skripten gewidmet.
Begonnen haben wir mit den Dateien: wie man sie umbenennt, wie man für sie Verknüpfungen erstellt und wie man sie verschiebt - ganz so wie man mit Dateien von der Befehlszeile aus oder in einem grafischen Dateimanager arbeiten würde.
Wenn man Dateien verwalten kann, sollte man auch in der Lage sein, Verzeichnisse zu verwalten. In der zweiten Hälfte dieser Lektion ging es daher darum, wie man Verzeichnisse anlegt und löscht, das aktuelle Arbeitsverzeichnis ausgibt oder wechselt und wie man Listen von Dateien aus einem Verzeichnis einliest (entweder durch Datei-Globbing oder durch Lesen aus einem Verzeichnis-Handle).
Frage:
Ich versuche, ein portierbares Skript zu schreiben, das eine config
-Datei einliest.
Ich kann in den Pfadnamen für Unix und Windows den Schrägstrich verwenden,
aber der Mac mit seinen Doppelpunkten in den Pfadangaben macht die Sache
doch sehr kompliziert. Wie kann ich das Problem lösen?
Antwort:
Sie können mit Hilfe eines Tests feststellen, ob Sie Ihr Skript auf einem Mac
ausführen, und dann das Pfad-Trennzeichen ordnungsgemäß einrichten (und
Ihren Dateipfad durch das Aneinanderhängen von Strings aufbauen).
Verwenden Sie das Config
-Modul, um herauszufinden, auf welcher Plattform
Sie sich befinden:
use Config;
if ( $Config{'osname'} =~ /^macos/i ) {
# ... Macintosh-spezifische Anweisungen
}
Frage:
Ich arbeite auf einem Unix-Rechner. Ich habe ein Skript getestet, das den Inhalt
einer Datei einliest und die Datei dann löscht. Da ich wollte, dass die Datei erst
gelöscht wird, nachdem das Skript debuggt worden ist, habe ich für die Datei die
Zugriffsberechtigung zum Schreiben gelöscht. Trotzdem hat Perl die Datei einfach
gelöscht. Warum?
Antwort:
Die Zugriffsberechtigungen einer Datei legen lediglich fest, ob Sie den Inhalt
der Datei lesen und schreiben können oder nicht. Ob die Datei umbenannt,
verschoben oder geändert werden kann, hängt von den
Zugriffsberechtigungen des betreffenden Verzeichnisses ab. Wenn Sie in Perl
verhindern wollen, dass Dateien gelöscht werden, müssen Sie die
Schreibberechtigung für das Verzeichnis löschen, in dem sich die Datei
befindet. Oder - noch besser - kommentieren Sie einfach Ihren unlink
-Befehl
aus, bis Sie den Rest Ihres Skripts debuggt haben. Unter http://
www.perl.com/CPAN-local/doc/FMTEYEWTK/file-dir-perms
finden Sie eine
Seite, auf der dieses Problem detailliert beschrieben ist.
Frage:
Sie haben beschrieben, wie man eine Datei umbenennt, eine Verknüpfung zu
einer Datei erstellt und Dateien entfernt. Wie jedoch kopiert man eine Datei?
Antwort:
Sie können dazu beide Dateien öffnen (die Datei, die kopiert werden soll, und
die Datei, in die kopiert werden soll) und dann die Zeilen aus der einen Datei
auslesen und in die andere ausgeben. Oder Sie nutzen die schrägen
Anführungszeichen, um den Kopierbefehl des Systems aufzurufen (wie Sie
noch in Kapitel 18 lernen werden). Oder Sie verwenden das Modul
File::Copy
, das Ihnen eine Kopierfunktion zur Verfügung stellt, mit der Sie
Dateien oder Datei-Handles von einer Quelle in ein Ziel kopieren können.
Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, und Übungen, die Sie anregen sollen, das eben Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Versuchen Sie, das Quiz und die Übungen zu beantworten und zu verstehen, bevor Sie zur Lektion des nächsten Tages übergehen.
`pwd`
und cwd()
? Warum sollten Sie die eine
Form der anderen vorziehen?
<*>
und einer Liste
von Dateien, die Sie mit readdir()
erhalten?
mkdir
?
/
) aus.
while (<foo*>) {
unlink $_;
}
#!/usr/bin/perl -w
opendir(DIR, '.') or die "Verzeichnis kann nicht geöffnet werden ($!)\n";
while (readdir(DIR)) {
print "$_\n";
}
closedir(DIR);
Hier die Antworten auf die Workshop-Fragen aus dem vorigen Abschnitt.
`pwd`
verwendet schräge Anführungszeichen, um den Unix-Befehl pwd
aufzurufen. Während manche Perl-Versionen auf anderen Systemen diesen Aufruf
ebenfalls korrekt verarbeiten (namentlich MacPerl), gilt dies bei weitem nicht für
alle Perl-Versionen. Soll Ihr Skript problemlos portierbar sein, verwenden Sie das
Modul Cwd
und die Funktion cwd()
. Denken Sie daran, dass Sie Cwd
erst
importieren müssen (use Cwd
), bevor Sie es verwenden können.
*.pl
zum Beispiel ist ein Glob, der alle
Dateien in einem Verzeichnis zurückliefert, die die Extension .pl
aufweisen. Globs
sind besonders nützlich, wenn man einen Satz von Dateien aufgreifen und
auslesen möchte, ohne explizit das Verzeichnis-Handle zu öffnen, die Liste zu
lesen und die Dateien auszusortieren, die dem Muster entsprechen.
<*>
liefert alle Dateien in einem Verzeichnis, abgesehen von den versteckten
Dateien, den Dateien, die mit ».«
starten, und den Verzeichnissen ».«
und »..«.
Verzeichnis-Handles liefern alle diese Dinge zurück.
mkdir
legt fest, welche Zugriffsberechtigung das neue
Verzeichnis haben soll. Unter Unix folgen diese Zugriffsberechtigungen den
normalen Format für Lese-, Schreib- und Ausführungszugriff für Besitzer, Gruppe
oder Welt. Für Mac oder Windows ist das Argument der Zugriffsberechtigung
ohne Bedeutung, dennoch müssen Sie es mit angeben (verwenden Sie einfach
0777
, und Sie bekommen keine Schwierigkeiten).
#!/usr/bin/perl -w
use strict;
use Cwd;
my $curr = cwd();
opendir(CURRDIR, $curr) or
die "Verzeichnis kann nicht geöffnet werden ($!)\n";
my @files = readdir(CURRDIR);
closedir(CURRDIR);
foreach my $file (sort @files) {
if (-d $file) {
print "$file/\n";
}
else { print "$file\n"; }
}
unlink
ist nur für das Löschen von Dateien gedacht. Verzeichnisse
werden mit rmdir
gelöscht. Geht es darum, alle Dateien und Verzeichnisse zu
entfernen, müssen Sie die Dateinamen einzeln daraufhin prüfen, ob es sich um
eine Datei oder ein Verzeichnis handelt, und dann die entsprechende Funktion
aufrufen.
while
-Schleife. Fälschlicherweise wurde
angenommen, dass readdir
in einer while
-Schleife den nächsten
Verzeichniseintrag der Variablen $_
zuweist - genauso wie dies beim Einlesen
einer Eingabezeile von einem Datei-Handle der Fall ist. Dies stimmt jedoch nicht.
Für readdir
müssen Sie explizit den nächsten Verzeichniseintrag einer Variablen
zuweisen:
while (defined($in = readdir(DIR))) { ... }
&getfilename()
, die die Fehlerprüfung durchführt, um sicherzustellen, dass die
Datei, die angegeben wurde, auch existiert (zum Umbenennen oder Löschen einer
Datei) oder gerade nicht existiert (zum Erzeugen einer neuen Datei).
#!/usr/bin/perl -w
use strict;
my $choice = '';
my @files = &getfiles();
while ($choice !~ /q/i) {
$choice = &printmenu();
SWITCH: {
$choice =~ /^1/ && do { &createfile(); last SWITCH; };
$choice =~ /^2/ && do { &renamefile(); last SWITCH; };
$choice =~ /^3/ && do { &deletefile(); last SWITCH; };
$choice =~ /^4/ && do { &listfiles(); last SWITCH; };
$choice =~ /^5/ && do { &printhist(); last SWITCH; };
}
}
sub printmenu {
my $in = "";
print "Wählen Sie einen Befehl (or Q to quit): \n";
print "1. Datei erzeugen\n";
print "2. Datei umbenennen\n";
print "3. Datei loeschen\n";
print "4. Alle Dateien auflisten\n";
while () {
print "\nIhre Wahl --> ";
chomp($in = <STDIN>);
if ($in =~ /^[1234]$/ || $in =~ /^q$/i) {
return $in;
} else {
print "Keine gueltige Wahl. 1-4 oder Q, bitte,\n";
}
}
}
sub createfile {
my $file = &getfilename(1);
if (open(FILE, ">$file")) {
print "Datei $file wurde erzeugt.\n\n";
@files = &getfiles();
close(FILE);
} else {
warn "$file konnte nicht geoeffnet werden ($!).\n";
}
}
sub renamefile {
my $file = &getfilename();
my $in = '';
print "Geben Sie den neuen Dateinamen ein: ";
chomp($in = <STDIN>);
if (rename $file,$in) {
print "Datei $file wurde in $in umbenannt.\n";
@files = &getfiles();
} else {
warn "$file konnte nicht umbenannt werden ($!).\n";
}
}
sub deletefile {
my $file = &getfilename();
if (unlink $file) {
print "Datei $file wurde geloescht.\n";
@files = &getfiles();
} else {
warn "$file konnte nicht geloescht werden ($!).\n";
}
}
sub getfilename {
my $in = '';
# ohne Argumente aufrufen, um sicherzustellen, dass die
# Datei existiert
my $new = 0;
# mit Argument aufrufen, um sicherzustellen, dass die
# Datei nicht existiert
if (@_) {
$new = 1;
}
while (!$in) {
print "Geben Sie einen Dateinamen ein (? fuer Liste): ";
chomp($in = <STDIN>);
if ($in eq '?') {
&listfiles();
$in = '';
} elsif ((grep /^$in$/, @files) && $new) {
# Datei existiert, keine neue Datei, OK
# Datei existiert, neue Datei: Fehler
print "Datei ($in) existiert bereits.\n";
$in = '';
} elsif ((!grep /^$in$/, @files) && !$new) {
# Datei existiert nicht, neue Datei, OK
# Datei existiert nicht, keine neue Datei: Fehler
print "Datei ($in) nicht gefunden.\n";
$in = '';
}
}
return $in;
}
sub getfiles {
my $in = '';
opendir(CURRDIR, '.') or
die "Verzeichnis konnte nicht geoeffnet werden ($!),
exiting.\n";
@files = readdir(CURRDIR);
closedir(CURRDIR);
return @files;
}
sub listfiles {
foreach my $file (@files) {
if (-f $file) { print "$file\n"; }
}
print "\n";
}